﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HSDc.RingleFlow.Api.Service;
using HSDc.RingleFlow.Kernel.DBPersit.Util;
using NUnit.Framework;
using HSDc.RingleFlow.Api.Rule;
using HSDc.RingleFlow.Api.Kernel;
using System.Xml;

namespace HSDc.RingleFlow.Kernel.DBPersit
{
    //[TestFixture]
    public class RepositoryServiceImpl : IRepositoryService
    {
        string IRepositoryService.RegisterProcessDefinition(string DefinitionXml)
        {
            Dao.RepositoryServiceDao dao = new Dao.RepositoryServiceDao();
            HSDc.RingleFlow.Api.Data.ProcessDefinition pdDs = ConvertFromXml(DefinitionXml);
            HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.ProcessDefinition pd =
                ConvertFromDataSet(pdDs);
            if (pd == null)
                return null;
            HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.ProcessDefinition existPd =
                dao.QueryProcessDefinition(pd.Name);
            if (existPd != null)
            {
                pd.Version = existPd.Version + 1;
            }
            dao.SaveProcessDefinition(pd);
            return pd.Id;
        }

        private HSDc.RingleFlow.Api.Data.ProcessDefinition ConvertFromXml(
            string DefinitionXml)
        {
            HSDc.RingleFlow.Api.Data.ProcessDefinition pdDs =
                new HSDc.RingleFlow.Api.Data.ProcessDefinition();
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(DefinitionXml);
            byte[] buf =
                System.Text.ASCIIEncoding.ASCII.GetBytes(doc.OuterXml);
            System.IO.MemoryStream ms = new System.IO.MemoryStream(buf);
            pdDs.ReadXml(ms, System.Data.XmlReadMode.InferSchema);
            ms.Close();
            return pdDs;
        }

        private HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.ProcessDefinition
            ConvertFromDataSet(HSDc.RingleFlow.Api.Data.ProcessDefinition pdDs)
        {
            if (pdDs._ProcessDefinition.Rows.Count == 0)
                return null;
            HSDc.RingleFlow.Api.Data.ProcessDefinition.ProcessDefinitionRow pdRow
                = pdDs._ProcessDefinition.ToList()[0];
            HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.ProcessDefinition pd =
                new Dto.ProcessDefinition.ProcessDefinition() 
                { 
                    Id = IdGenerator.NewId(),
                    Name = pdRow.DefinitionName,
                    Enabled = pdRow.DefinitionStatus == 1,
                    Version = 1
                };
            List<HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.Swimlane> sls =
                ProcessSwimLane(pdRow.GetSwimlaneRows());
            pd.Swimlanes = sls;
            List<Dto.ProcessDefinition.Activity> acs = ProcessActivity(pdRow.GetActivityRows(), sls);
            ProcessActivityTransition(ref acs, pdRow.GetActivityRows());
            pd.Activities = acs;
            return pd;
        }

        private List<HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.Swimlane>
            ProcessSwimLane(HSDc.RingleFlow.Api.Data.ProcessDefinition.SwimlaneRow[] rows)
        {
            List<HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.Swimlane> sls = new List<Dto.ProcessDefinition.Swimlane>();
            if (rows == null)
                return sls;
            for (int i = 0; i < rows.Length; i++)
            {
                sls.Add(new Dto.ProcessDefinition.Swimlane()
                {
                    Id = IdGenerator.NewId(),
                    Seq = rows[i].SeqNo,
                    Name = rows[i].Name,
                    RuleClassName = rows[i].RuleRow.RuleClass
                });
            }
            return sls;
        }

        private List<HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.Activity>
            ProcessActivity(Api.Data.ProcessDefinition.ActivityRow[] acRow,
            List<HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.Swimlane> sls)
        {
            List<HSDc.RingleFlow.Kernel.Dto.ProcessDefinition.Activity> acs = new List<Dto.ProcessDefinition.Activity>();
            if (acRow == null)
                return acs;
            for (int i = 0; i < acRow.Length; i ++)
            {
                Dto.ProcessDefinition.Activity ac = new Dto.ProcessDefinition.Activity()
                {
                    Id = IdGenerator.NewId(),
                    Name = acRow[i].Name,
                    Type = (Dto.ProcessDefinition.ActivityType)
                        Enum.Parse(typeof(Dto.ProcessDefinition.ActivityType), acRow[i].ActivityClass, true),
                    RuleClassName = acRow[i].RuleRow == null ? null : acRow[i].RuleRow.RuleClass,
                    Swimlane = sls.Where(s => s.Seq == acRow[i].SwimlaneRow.SeqNo).SingleOrDefault()
                };
                acs.Add(ac);

            }
            return acs;
        }

        private void ProcessActivityTransition(
            ref List<Dto.ProcessDefinition.Activity> acs,
            Api.Data.ProcessDefinition.ActivityRow[] acRow)
        {
            foreach(var ac in acs)
            {
                Api.Data.ProcessDefinition.TransitionRow[] tRows = 
                    acRow.Where(a => a.Name == ac.Name).SingleOrDefault().GetTransitionRowsByActivity_Transition_Source();
                List<Dto.ProcessDefinition.Transition> trans = new List<Dto.ProcessDefinition.Transition>();
                for (int j = 0; j < tRows.Length; j++)
                {
                    trans.Add(new Dto.ProcessDefinition.Transition()
                    {
                        Id = IdGenerator.NewId(),
                        Source = ac,
                        Target = acs.Where(a => a.Name == tRows[j].TargetActivityName).SingleOrDefault()
                    });
                }
                ac.OutboundTransitions = trans;
            }
        }

        Api.Kernel.IProcessDefinition IRepositoryService.GetProcessDefinition(string processDefinitionId)
        {
            Dao.RepositoryServiceDao dao = new Dao.RepositoryServiceDao();
            ProcessDefinitionDto dto = dao.GetProcessDefinition(processDefinitionId);
            return InitialProcessDefinition(dto, dao);
        }

        Api.Kernel.IProcessDefinition IRepositoryService.GetProcessDefinitionByName(string processDefinitionName)
        {
            Dao.RepositoryServiceDao dao = new Dao.RepositoryServiceDao();
            ProcessDefinitionDto dto = dao.GetProcessDefinitionByName(processDefinitionName);
            return InitialProcessDefinition(dto, dao);
        }

        private ProcessDefinition InitialProcessDefinition(ProcessDefinitionDto dto, Dao.RepositoryServiceDao dao)
        {
            ProcessDefinition _pd = new ProcessDefinition(dto.Definition_GUID, dto.Definition_Name, dto.Definition_Version.ToString(), dto.Definition_ACTIVE,
                dao.GetActiveProcessInstance(dto.Definition_ID));
            List<ProcessActivityDto> actDtos = dao.GetDefaultActivities(dto.Definition_ID);
            SortedDictionary<int, Activity> actDict = new SortedDictionary<int, Activity>();
            SortedDictionary<int, Transition> transDict = new SortedDictionary<int, Transition>();
            Dictionary<int, TransitionDto> transDtoDict = new Dictionary<int, TransitionDto>();
            CreateActivityAndTransition(dao, actDtos, actDict, transDict, transDtoDict);
            FillTransitionToActivity(actDict, transDict, transDtoDict);
            foreach (Activity act in actDict.Values)
            {
                _pd.AddActivity(act);
            }
            return _pd;
        }

        private static void FillTransitionToActivity(SortedDictionary<int, Activity> actDict, SortedDictionary<int, Transition> transDict, Dictionary<int, TransitionDto> transDtoDict)
        {
            Activity acin, acout = null;
            TransitionDto transDto2 = null;
            Transition trans = null;
            transDict.Reverse();
            foreach (int transId in transDict.Keys)
            {
                transDtoDict.TryGetValue(transId, out transDto2);
                transDict.TryGetValue(transId, out trans);
                actDict.TryGetValue(transDto2.Transition_Target, out acin);
                trans.AddTarget(acin);
                actDict.TryGetValue(transDto2.Transition_Source, out acout);
                acout.AddOutboundTransition(trans);
                acin.AddInboundTransition(trans);
                actDict.Remove(transDto2.Transition_Target);
                actDict.Add(transDto2.Transition_Target, acin);
                actDict.Remove(transDto2.Transition_Source);
                actDict.Add(transDto2.Transition_Source, acout);
            }
        }

        private void CreateActivityAndTransition(Dao.RepositoryServiceDao dao, List<ProcessActivityDto> actDtos, SortedDictionary<int, Activity> actDict, SortedDictionary<int, Transition> transDict, Dictionary<int, TransitionDto> transDtoDict)
        {
            Activity ac = null;
            Transition trans = null;
            foreach (ProcessActivityDto actDto in actDtos)
            {
                ac = WFObjectFactory.NewActivity(this, actDto.Activity_CLASS, new object[] { actDto.Activity_GUID, actDto.Activity_Name, 
                    (IRule)WFObjectFactory.NewRule(this, actDto.Activity_RULE_CLASS, new object[] {}), 
                    RetrieveActivitySwimlane(dao, actDto.Activity_ID)});
                actDict.Add(actDto.Activity_ID, ac);
                foreach (TransitionDto transDto in dao.GetTransitionsBySource(actDto.Activity_ID))
                {
                    trans = new Transition(ac, transDto.Transition_Constraints);
                    transDict.Add(transDto.Transition_ID, trans);
                    transDtoDict.Add(transDto.Transition_ID, transDto);
                }
            }
        }

        private Swimlane RetrieveActivitySwimlane(Dao.RepositoryServiceDao dao, int ActivityID)
        {
            SwimlaneDto dto = dao.GetActivityBelongSwimlane(ActivityID);
            return new Swimlane(dto.Swimlane_GUID, dto.Swimlane_Org_ID,
                RetrieveSwimlaneType(dao, dto.Swimlane_GUID),
                (IRule)WFObjectFactory.NewRule(this, dto.Swimlane_RULE_CLASS, new object[] { }));

        }

        private SwimlaneType RetrieveSwimlaneType(Dao.RepositoryServiceDao dao, string guid)
        {
            int orgType = dao.GetOrgType(guid);
            return (SwimlaneType)orgType;
        }

        [Test]
        public void TestInitialProcessDefinition()
        {
            Dao.RepositoryServiceDao dao = new Dao.RepositoryServiceDao();
            ProcessDefinitionDto dto = dao.GetProcessDefinition("83CEDA43-0E89-4F57-95D7-553FCF721A38");
            ProcessDefinition real = InitialProcessDefinition(dto, dao);
        }
    }
}
